#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define T Table_T
typedef struct T *T;

#define szMax 512

typedef int comparePtr(const char*, const char*);

struct T{
    struct node{
        char key[szMax];
        void *value;
        struct node *next;
        } *node;
    comparePtr *compare;
    int cnt;
};


struct node *newNode(char *str)
{
    struct node* tnode = (struct node*) malloc(sizeof(struct node));
    strcpy(tnode->key, str);
    tnode->next = NULL;
    return(tnode);
}

T Table_new(int hint, int compare(const char *x, const char *y))
{
    T table = calloc(1, sizeof *table);
    assert(table);
    table->compare=compare;
    table->cnt = hint;
    int i = 0;
    struct node* tnode = (struct node*) malloc(hint * sizeof(struct node));
    table->node = tnode;
    for(i = 0; i<hint; i++)
    {
        tnode->next=++tnode;
    }
    //table->node = tnode;

    return table;
}

void Table_free(T *table)
{
    int i = 0;
    T p = *table;
    struct node* pnode;
    pnode = p->node;
    
    for(i = 0; i < p->cnt; i++)
    {
        pnode->value = NULL;
        free(pnode->value);
        free(pnode++);

    }
    free(p->node);
    free(p);
    p = NULL;
    *table = NULL;
}

static struct node* insert(struct node* node, char *str,void *value, int compare(const char*, const char*), int size)
{
    int i = 0;
    struct node* pnode;
    pnode = node;
    if(pnode == NULL)
    {
        fprintf(stderr, "%s", "DONT PASS THE NULL HERE!\n");
    }
    else
    {
        for(i = 0; i < size; i++)
        {
            if((pnode->key[0] != '\0') && (compare(str, pnode->key)== 0))
            {
                //fprintf(stderr, "%s\n", "REPLACED!");
                //REPLACE
                pnode->value = value;
                pnode++;
                break;
            }
            else
                {
                    if(pnode->key[0] == '\0')
                    {
                        //fprintf(stderr, "%s\n", "COPIED NEW!");
                        //COPY
                        strcpy(pnode->key, str);
                        pnode->value=value;
                        pnode++;
                        break;
                    }
                }
            pnode++;
        }
    }
    return pnode;
}


void Table_put(T table, char *key, void *value)
{
    int i = 0;
    if((key[0] != '\0')&& (key[0] != EOF) && (value != NULL))
    {
        //table->node =
        insert(table->node, key, value, table->compare, table->cnt);
        //fprintf(stdout, "Table_put: key = %s\n", key);
    }
}

void *Table_get(T table, char *key)
{
	struct node* pnode;
	pnode = table->node;
	
	int i = 0;
	for(i = 0; i < table->cnt; i++)
	{
		//fprintf(stdout, "get - key = %d, %ld\n", i, sizeof(pnode));
		if(table->compare(pnode->key, key)==0)
		{
			return pnode->value;
			break;
		}
		pnode++;
	}
	//fprintf(stdout, "get - keyMM = %s\n", key);
	return NULL;
}

int Table_length(T table)
{
	struct node *pnode;
	pnode = table->node;
	int i = 0;
	while(pnode->key[0]!='\0' && pnode->value != NULL)
	{
		i++;
		pnode++;
	}
	return i;
}

void Table_map(T table, void apply(char *key, void **value, void *cl), void *cl)
{
    int i = 0;
    for(i = 0; i < table->cnt; i++)
    {
        apply(table->node->key, &table->node->value, cl);
    }
}

//------------------------------------------------------------------------------
// Just for print out
//------------------------------------------------------------------------------
#define L LIST
typedef struct L *L;

    struct L{
        struct lnode{
            char key[szMax];
            int value;
            } *node;
        int length;
    };
    
int scompare(const void * a, const void * b)
{
    struct lnode* pa;
    struct lnode* pb;
    pa = (struct lnode*)a;
    pb = (struct lnode*)b;
    return strcmp(pa->key, pb->key);
}

void Table_print(T table, int val, int sort)
{
    //fprintf(stderr, "...Table_print()\n");
    int i = 0;
    int ab;
    struct node* pnode;
    struct node* qnode;
    pnode = table->node;
    //qnode = table->node;
    
    L list = calloc(1, sizeof *list);
    list->length = 0;

    for(i = 0; (i < table->cnt) && (pnode->value != NULL); i++)
    {
        memcpy(&ab, pnode->value, sizeof(ab));
        if(val > 0 && ab > val)   //+n
        {
            list->length += 1;
            pnode++;
        } else if (val < 0 &&  ab <(-1 * val))
        {
            list->length += 1;
            pnode++;
        } else if (val == 0)
        {
            list->length += 1;
            pnode++;
        }
    }

    struct lnode* tnode = (struct lnode*) malloc(list->length * sizeof(struct lnode));
    
    int j = 0;
    pnode = table->node;
    for(i = 0; (i < table->cnt) && (pnode->value != NULL); i++)
    {
        memcpy(&ab, pnode->value, sizeof(ab));
        if(val > 0 && ab > val)   //+n
        {
            strcpy(tnode[j].key, pnode->key);
            tnode[j++].value = ab;
            pnode++;
        } else if (val < 0 &&  ab <(-1 * val))
        {
            strcpy(tnode[j].key, pnode->key);
            tnode[j++].value = ab;
            pnode++;
        } else if (val == 0)
        {
            strcpy(tnode[j].key, pnode->key);
            tnode[j++].value = ab;
            pnode++;
        }
    }
    
    if(sort != 0)
    {
        qsort(tnode, list->length, sizeof(struct lnode), scompare);
    }
   
    for(i = 0; i < list->length; i++)
    {
        fprintf(stdout, "%s \t-\t %d\n", tnode[i].key, tnode[i].value);
    }
    
    //fprintf(stderr, "___Table_print()\n");
}
